﻿using System;
using System.Collections.Generic;
using System.Text;
using SdmxMl.Manager;
using SdmxMl.Structure;
using SdmxMl.Common;

namespace SdmxDiff
{
    public enum DiffType
    {
        None,
        New,
        Modified,
    }

    public class AnnotationListDiff
    {
        public DicoTypeDiff DicoDiff { get; private set; }

        private AnnotationList annListOld;
        private AnnotationList annListNew;

        public bool HasDiff { get; private set; }

        public AnnotationListDiff(AnnotationList anOld, AnnotationList anNew)
        {
            annListNew = anNew;
            annListOld = anOld;
            Initialize (new List<string>(LangSupport.SupportedLanguages));
        }
        public AnnotationListDiff(AnnotationList anOld, AnnotationList anNew, string lg)
        {
            annListNew = anNew;
            annListOld = anOld;
            Initialize(new List<string>(new string[] { lg }));
        }

        private void Initialize(List<string> targetLang)
        {
            List<string> targetLangList = new List<string>(LangSupport.SupportedLanguages);


            // Make a dictionnary of all type list for old and new
            DicoDiff = new DicoTypeDiff();
            foreach (Annotation a in annListOld)
            {
                if (DicoDiff.ContainsKey(a.Type) == false)
                    DicoDiff.Add(a.Type, new OldNewTypeList());
                DicoDiff[a.Type].anListOld.Add(new AnnotationMatch(a));
            }
            foreach (Annotation a in annListNew)
            {
                if (DicoDiff.ContainsKey(a.Type) == false)
                    DicoDiff.Add(a.Type, new OldNewTypeList());
                DicoDiff[a.Type].anListNew.Add(new AnnotationMatch(a));
            }

            // mark as same matching one in each type
            foreach (OldNewTypeList ontl in DicoDiff.Values)
            {
                foreach (AnnotationMatch amn in ontl.anListNew)
                {
                    if (amn.match)
                        continue;
                    foreach (AnnotationMatch amo in ontl.anListOld)
                    {
                        if (amo.match)
                            continue;

                        if (amo.Annot.Title == amn.Annot.Title &&
                           amo.Annot.Url == amn.Annot.Url &&
                           string.IsNullOrEmpty(
                           (new MultilangDiff(amo.Annot.Text, amn.Annot.Text, targetLangList)).DiffComment))
                        {
                            amo.match = amn.match = true;
                            amn.MatchedAnnot = amo.Annot;
                            amo.MatchedAnnot = amn.Annot;
                            break;
                        }
                    }
                }
            }



        }
    }

    public class OldNewTypeList
    {
        public List<AnnotationMatch> anListOld;
        public List<AnnotationMatch> anListNew;

        public OldNewTypeList()
        {
            anListOld = new List<AnnotationMatch>();
            anListNew = new List<AnnotationMatch>();
        }
    }

    public class AnnotationMatch
    {
        public Annotation Annot;
        public bool match;
        public Annotation MatchedAnnot;

        public AnnotationMatch(Annotation a)
        {
            Annot = a;
            match = false;
        }
    }

    public class DicoTypeDiff : Dictionary<string, OldNewTypeList>
    {
        public bool HasDiff
        {
            get
            {
                foreach (OldNewTypeList ontl in Values)
                {
                    if (ontl.anListNew.Count != ontl.anListOld.Count)
                        return true;
                    foreach (AnnotationMatch amo in ontl.anListNew)
                        if (amo.match == false)
                            return true;
                }
                return false;
            }
        }

        public DiffType NewDiffKind(string Type, Annotation a)
        {
            DiffType diff = DiffType.None;

            if (ContainsKey(Type))
            {
                OldNewTypeList ontl = this[Type];
                {
                    for (int i = 0; i < ontl.anListNew.Count; ++i)
                    {
                        if (ontl.anListNew[i].Annot == a) 
                        {
                            if (ontl.anListNew[i].match == false)
                            {
                                // assume new if bigger index than in old list. If not assume mutation
                                diff = i >= ontl.anListOld.Count ? DiffType.New : DiffType.Modified;
                            }
                            break;
                        }
                    }
                }
            }
            return diff;
        }
        public List<string> AddedTypes
        {
            get
            {
                 List<string> list = new List<string>();
                 foreach (OldNewTypeList ontl in Values)
                 {
                     if (ontl.anListNew.Count > 0 && ontl.anListOld.Count == 0)
                         list.Add(ontl.anListNew[0].Annot.Type);
                 }
                 return list;
           }
        }

        public List<string> RemovedTypes
        {
            get
            {
                List<string> list = new List<string>();
                foreach (OldNewTypeList ontl in Values)
                {
                    if (ontl.anListNew.Count == 0 && ontl.anListOld.Count > 0)
                        list.Add(ontl.anListOld[0].Annot.Type);
                }
                return list;
            }
        }


    }
}
